package com.byd.utils;

import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.PorterDuff;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader.TileMode;

public final class BitmapUtils {
	private static Paint sMirrirPaint;
	private static int sMirrorHeight;
	private static Paint sScalePaint;
	private static Canvas sScaleCanvas;
	public static final Object mBitmapLock = new Object();

	private static Paint getMirrorPaint(Bitmap bmp) {
		if(sMirrorHeight != bmp.getHeight()) {
			sMirrorHeight = bmp.getHeight();
			final LinearGradient gradient = new LinearGradient(0, sMirrorHeight, 0, sMirrorHeight * 3 / 2, 0xee000000,
					0x00000000, TileMode.CLAMP);
			if(sMirrirPaint == null) {
				sMirrirPaint = new Paint();
			}
			sMirrirPaint.setShader(gradient);
			sMirrirPaint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
		}
		return sMirrirPaint;
	}

	private static Paint getScalePaint() {
		if(sScalePaint == null) {
			sScalePaint = new Paint();
			sScalePaint.setFlags(Paint.ANTI_ALIAS_FLAG);
			sScalePaint.setFilterBitmap(true);
		}
		return sScalePaint;
	}

	private static Canvas getScaleCanvas(Bitmap bmp) {
		if(sScaleCanvas == null) {
			sScaleCanvas = new Canvas();
		}
		sScaleCanvas.setBitmap(bmp);
		return sScaleCanvas;
	}

	public static Bitmap getBitmap(byte[] data, int width, int height, boolean scale) {
		synchronized (mBitmapLock) {
			final Bitmap bmp = BitmapFactory.decodeByteArray(data, 0, data.length, null);
			final int w = bmp.getWidth();
			final int h = bmp.getHeight();
			Bitmap result;
			if(width > 0 && height > 0) {
				if(scale) {
					result = scaleBitmap(bmp, width, height);
				}
				else {
					// TODO 不是scale时
					result = scaleBitmap(bmp, width, height);
				}
				// bmp.recycle();
			}
			else {
				result = bmp;
			}
			Lg.d("getBitmap: " + data.length + ", [" + w + ", " + h + "] --> [" + result.getWidth() + ", "
					+ result.getHeight() + "]");
			return result;
		}
	}

	public static Bitmap getBitmap(byte[] data) {
		synchronized (mBitmapLock) {
			return BitmapFactory.decodeByteArray(data, 0, data.length);
		}
	}

	public static Bitmap scaleBitmap(Bitmap bmp, int width, int height) {
		synchronized (mBitmapLock) {
			if(width <= 0 || height <= 0) {
				return bmp;
			}
			Bitmap result = bmp;
			final int w = bmp.getWidth();
			final int h = bmp.getHeight();
			if(w != width || h != height) {
				result = Bitmap.createBitmap(width, height, Config.ARGB_8888);
				final Paint paint = getScalePaint();
				final Canvas canvas = getScaleCanvas(result);
				final Matrix matrix = new Matrix();
				matrix.setScale(width * 1.0f / w, height * 1.0f / h);
				canvas.drawBitmap(bmp, matrix, paint);
			}
			return result;
		}
	}

	public static Bitmap roundBitmap(Bitmap bmp, float radius, int stroke) {
		synchronized (mBitmapLock) {
			final Bitmap outBitmap = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), Bitmap.Config.ARGB_8888);
			final Canvas canvas = new Canvas();
			canvas.setBitmap(outBitmap);
			final Paint paint = new Paint();
			paint.setAntiAlias(true);
			final Rect rect = new Rect(0, 0, outBitmap.getWidth(), outBitmap.getHeight());
			final RectF rectF = new RectF(rect);
			canvas.drawRoundRect(rectF, radius, radius, paint);
			paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
			canvas.drawBitmap(bmp, null, rect, paint);
			if(stroke > 0) {
				paint.reset();
				paint.setStrokeWidth(stroke);
				paint.setColor(Color.rgb(0x23, 0x24, 0x24));
				paint.setStyle(Style.STROKE);
				paint.setAntiAlias(true);
				canvas.drawRoundRect(rectF, radius, radius, paint);
			}
			return outBitmap;
		}
	}

	public static Bitmap mirrorBitmap(Bitmap bmp, boolean x, boolean y) {
		synchronized (mBitmapLock) {
			final Matrix matrix = new Matrix();
			matrix.postScale(x ? -1 : 1, y ? -1 : 1);
			final Bitmap outBitmap = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrix, true);
			return outBitmap;
		}
	}

	public static Bitmap connectMirrorBitmap(Bitmap bmp) {
		synchronized (mBitmapLock) {
			final Bitmap outBitmap = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight() * 3 / 2, Config.ARGB_8888);
			final Canvas canvas = new Canvas(outBitmap);
			canvas.drawBitmap(bmp, 0, 0, null);
			Bitmap mirror = mirrorBitmap(bmp, false, true);
			mirror = BitmapUtils.doBlur(mirror, 5, true);
			canvas.drawBitmap(mirror, 0, bmp.getHeight(), null);
			canvas.drawRect(0, bmp.getHeight(), bmp.getWidth(), outBitmap.getHeight(), getMirrorPaint(bmp));
			return outBitmap;
		}
	}

	public static Bitmap doBlur(Bitmap sentBitmap, int radius, boolean canReuseInBitmap) {
		synchronized (mBitmapLock) {
			Bitmap bitmap;
			if(canReuseInBitmap) {
				bitmap = sentBitmap;
			}
			else {
				bitmap = sentBitmap.copy(sentBitmap.getConfig(), true);
			}
			if(radius < 1) {
				return null;
			}
			final int w = bitmap.getWidth();
			final int h = bitmap.getHeight();
			final int[] pix = new int[w * h];
			bitmap.getPixels(pix, 0, w, 0, 0, w, h);
			final int wm = w - 1;
			final int hm = h - 1;
			final int wh = w * h;
			final int div = radius + radius + 1;
			final int r[] = new int[wh];
			final int g[] = new int[wh];
			final int b[] = new int[wh];
			int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
			final int vmin[] = new int[Math.max(w, h)];
			int divsum = div + 1 >> 1;
			divsum *= divsum;
			final int dv[] = new int[256 * divsum];
			for (i = 0; i < 256 * divsum; i++) {
				dv[i] = i / divsum;
			}
			yw = yi = 0;
			final int[][] stack = new int[div][3];
			int stackpointer;
			int stackstart;
			int[] sir;
			int rbs;
			final int r1 = radius + 1;
			int routsum, goutsum, boutsum;
			int rinsum, ginsum, binsum;
			for (y = 0; y < h; y++) {
				rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
				for (i = -radius; i <= radius; i++) {
					p = pix[yi + Math.min(wm, Math.max(i, 0))];
					sir = stack[i + radius];
					sir[0] = (p & 0xff0000) >> 16;
					sir[1] = (p & 0x00ff00) >> 8;
					sir[2] = p & 0x0000ff;
					rbs = r1 - Math.abs(i);
					rsum += sir[0] * rbs;
					gsum += sir[1] * rbs;
					bsum += sir[2] * rbs;
					if(i > 0) {
						rinsum += sir[0];
						ginsum += sir[1];
						binsum += sir[2];
					}
					else {
						routsum += sir[0];
						goutsum += sir[1];
						boutsum += sir[2];
					}
				}
				stackpointer = radius;
				for (x = 0; x < w; x++) {
					r[yi] = dv[rsum];
					g[yi] = dv[gsum];
					b[yi] = dv[bsum];
					rsum -= routsum;
					gsum -= goutsum;
					bsum -= boutsum;
					stackstart = stackpointer - radius + div;
					sir = stack[stackstart % div];
					routsum -= sir[0];
					goutsum -= sir[1];
					boutsum -= sir[2];
					if(y == 0) {
						vmin[x] = Math.min(x + radius + 1, wm);
					}
					p = pix[yw + vmin[x]];
					sir[0] = (p & 0xff0000) >> 16;
					sir[1] = (p & 0x00ff00) >> 8;
					sir[2] = p & 0x0000ff;
					rinsum += sir[0];
					ginsum += sir[1];
					binsum += sir[2];
					rsum += rinsum;
					gsum += ginsum;
					bsum += binsum;
					stackpointer = (stackpointer + 1) % div;
					sir = stack[stackpointer % div];
					routsum += sir[0];
					goutsum += sir[1];
					boutsum += sir[2];
					rinsum -= sir[0];
					ginsum -= sir[1];
					binsum -= sir[2];
					yi++;
				}
				yw += w;
			}
			for (x = 0; x < w; x++) {
				rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
				yp = -radius * w;
				for (i = -radius; i <= radius; i++) {
					yi = Math.max(0, yp) + x;
					sir = stack[i + radius];
					sir[0] = r[yi];
					sir[1] = g[yi];
					sir[2] = b[yi];
					rbs = r1 - Math.abs(i);
					rsum += r[yi] * rbs;
					gsum += g[yi] * rbs;
					bsum += b[yi] * rbs;
					if(i > 0) {
						rinsum += sir[0];
						ginsum += sir[1];
						binsum += sir[2];
					}
					else {
						routsum += sir[0];
						goutsum += sir[1];
						boutsum += sir[2];
					}
					if(i < hm) {
						yp += w;
					}
				}
				yi = x;
				stackpointer = radius;
				for (y = 0; y < h; y++) {
					// Preserve alpha channel: ( 0xff000000 & pix[yi] )
					pix[yi] = 0xff000000 & pix[yi] | dv[rsum] << 16 | dv[gsum] << 8 | dv[bsum];
					rsum -= routsum;
					gsum -= goutsum;
					bsum -= boutsum;
					stackstart = stackpointer - radius + div;
					sir = stack[stackstart % div];
					routsum -= sir[0];
					goutsum -= sir[1];
					boutsum -= sir[2];
					if(x == 0) {
						vmin[y] = Math.min(y + r1, hm) * w;
					}
					p = x + vmin[y];
					sir[0] = r[p];
					sir[1] = g[p];
					sir[2] = b[p];
					rinsum += sir[0];
					ginsum += sir[1];
					binsum += sir[2];
					rsum += rinsum;
					gsum += ginsum;
					bsum += binsum;
					stackpointer = (stackpointer + 1) % div;
					sir = stack[stackpointer];
					routsum += sir[0];
					goutsum += sir[1];
					boutsum += sir[2];
					rinsum -= sir[0];
					ginsum -= sir[1];
					binsum -= sir[2];
					yi += w;
				}
			}
			bitmap.setPixels(pix, 0, w, 0, 0, w, h);
			return bitmap;
		}
	}
}
